package com.delete.executors;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.kevin.datasource.DefaultDataSourceFactory;
import com.kevin.enums.DBType;
import com.kevin.enums.transaction.TransactionIsolationLevel;

public class MExecutor implements AutoCloseable {
	private static final Logger LOGGER = LoggerFactory.getLogger(MExecutor.class);

	private DataSource dataSource;
	private Connection conn = null;
	private DatabaseMetaData metaData = null;

	private DBType dbType = DBType.MYSQL;
	private char prefix = '@';
	private String executorName = StringUtils.EMPTY;

	public DBType getDbType() {
		return dbType;
	}

	public void setDbType(DBType dbType) {
		this.dbType = dbType;
	}

	public String getExecutorName() {
		return executorName;
	}

	public void setExecutorName(String executorName) {
		this.executorName = executorName;
	}

	public char getPrefix() {
		return prefix;
	}

	void setPrefix(char prefix) {
		this.prefix = prefix;
	}

	public DatabaseMetaData getMetaData() {
		return metaData;
	}

	public boolean isOpenTrans() {
		try {
			return !conn.getAutoCommit();
		} catch (SQLException e) {
			e.printStackTrace();
			return false;
		}
	}

	public MExecutor() {

	}

	public MExecutor(Properties properties) {
		DefaultDataSourceFactory dataSourceFactory = new DefaultDataSourceFactory();
		dataSourceFactory.setProperties(properties);
		dataSource = dataSourceFactory.getDataSource();
		open();
	}

	public MExecutor(String className, Properties properties) {
		DefaultDataSourceFactory dataSourceFactory = new DefaultDataSourceFactory(className);
		dataSourceFactory.setProperties(properties);
		dataSource = dataSourceFactory.getDataSource();
		open();
	}

	/**
	 * 执行sql语句
	 * 
	 * @param sql
	 * @return 返回ResultSet
	 */
	public ResultSet query(String sql) {
		try {
			PreparedStatement pstmt = conn.prepareStatement(sql);
			setQueryParameters(pstmt);
			LOGGER.debug("query: " + pstmt.toString().split(":")[1]);
			return pstmt.executeQuery();
		} catch (SQLException e) {
			close();
			String msg = "failed to execute query sql!";
			throw new ExecutorException(msg);
		}
	}

	/**
	 * 新增，删除，修改
	 * 
	 * @param sql
	 * @return
	 */
	public int update(String sql) {
		try {
			PreparedStatement pstmt = conn.prepareStatement(sql);
			setQueryParameters(pstmt);
			LOGGER.debug("update: " + pstmt.toString().split(":")[1]);
			return pstmt.executeUpdate();
		} catch (SQLException e) {
			close();
			String msg = "failed to execute update sql!";
			throw new ExecutorException(msg);
		}
	}

	public int updateBatch(String sql,int oneCount) {
		try {
			int sum = 0;
			PreparedStatement pstmt = conn.prepareStatement(sql);
			if (paramList.size() > 0) {
				ExeParameter param = null;
				for (int i = 0; i < paramList.size(); i++) {
					param = paramList.get(i);
					try {
						int count = i % oneCount;
						if(i > 0 && count == 0){
							pstmt.addBatch();
						}
						pstmt.setObject(count + 1, param.getValue(), param.getSqlType());
						
						if (i > 0 && (i+1) / oneCount > 1000) {
							LOGGER.debug("updateBatch: " + pstmt.toString().split(":")[1]);
							pstmt.addBatch();
							int[] retCount = pstmt.executeBatch();
							pstmt.clearBatch();
							for (int j : retCount) {
								sum += j;
							}
						}
					} catch (SQLException e) {
						String msg = "failed to add parameters to PreparedStatement!";
						throw new ExecutorException(msg);
					}
				}
				paramList.clear();
			}
			LOGGER.debug("updateBatch: " + pstmt.toString().split(":")[1]);
			pstmt.addBatch();
			int[] retCount = pstmt.executeBatch();
			for (int i : retCount) {
				sum += i;
			}
			return sum;
		} catch (SQLException e) {
			close();
			String msg = "failed to execute updateBatch sql!";
			throw new ExecutorException(msg);
		}
	}

	public void beginTransation(TransactionIsolationLevel level) {
		try {
			if (level != null) {
				conn.setTransactionIsolation(level.getLevel());
			}
			conn.setAutoCommit(false);
		} catch (SQLException e) {
			throw new ExecutorException("failed to begin transation!");
		}
	}

	public void endTransation() {
		try {
			conn.commit();
			conn.setAutoCommit(true);
		} catch (SQLException e) {
			throw new ExecutorException("failed to end transation!");
		}
	}

	/**
	 * 为查询条件设置参数
	 * 
	 * @param pstmt
	 */
	private void setQueryParameters(PreparedStatement pstmt) {
		if (paramList.size() == 0)
			return;

		ExeParameter param = null;
		for (int i = 0; i < paramList.size(); i++) {
			param = paramList.get(i);
			try {
				pstmt.setObject(i + 1, param.getValue(), param.getSqlType());
			} catch (SQLException e) {
				String msg = "failed to add parameters to PreparedStatement!";
				throw new ExecutorException(msg);
			}
		}
		paramList.clear();
	}

	private ArrayList<ExeParameter> paramList = new ArrayList<>();

	/**
	 * 将参数添加到缓存，查询时使用，查询完后进行清除
	 * 
	 * @param param
	 */
	public void addParameters(ExeParameter param) {
		paramList.add(param);
	}

	/**
	 * 清除缓存的参数
	 */
	public void clearParameters() {
		paramList.clear();
	}

	/**
	 * 查询参数的数量
	 * 
	 * @return
	 */
	public int parametersSize() {
		return paramList.size();
	}

	/**
	 * 打开executor
	 */
	public void open() {
		try {
			if (isClosed()) {
				conn = dataSource.getConnection();
				metaData = conn.getMetaData();
			}
		} catch (SQLException e) {
			close();
			String msg = "failed to open datasource connection!";
			throw new ExecutorException(msg);
		}
	}

	public PreparedStatement getPreparedStatement(String sql) {
		try {
			return conn.prepareStatement(sql);
		} catch (SQLException e) {
			close();
			String msg = "failed to open datasource connection!";
			throw new ExecutorException(msg);
		}
	}

	/**
	 * 检查executor是否已经被关闭
	 * 
	 * @return
	 */
	public boolean isClosed() {
		try {
			if (conn == null || conn.isClosed()) {
				return true;
			} else {
				return false;
			}
		} catch (SQLException e) {
			throw new ExecutorException(e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.AutoCloseable#close()
	 */
	@Override
	public void close() {
		if (conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				throw new ExecutorException(e);
			}
			conn = null;
		}
	}
}
